1 /** 2 * T-Tree. 3 * Copyright: © 2015 Economic Modeling Specialists, Intl. 4 * Authors: Brian Schott 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 8 module containers.ttree; 9 10 private import containers.internal.node : shouldAddGCRange; 11 private import containers.internal.mixins : AllocatorState; 12 private import std.experimental.allocator.mallocator : Mallocator; 13 14 /** 15 * Implements a binary search tree with multiple items per tree node. 16 * 17 * T-tree Nodes are (by default) sized to fit within a 64-byte 18 * cache line. The number of items stored per node can be read from the 19 * `nodeCapacity` enum. Each node has 0, 1, or 2 children. Each node has between 20 * 1 and `nodeCapacity` items, or it has `nodeCapacity` items and 0 or 21 * more children. 22 * 23 * Inserting or removing items while iterating a range returned from `opSlice`, 24 * `upperBound`, `equalRange`, or other similar functions will result in 25 * unpredicable and likely invalid iteration orders. 26 * 27 * Params: 28 * T = the element type 29 * Allocator = the allocator to use. Defaults to `Mallocator`. 30 * allowDuplicates = if true, duplicate values will be allowed in the tree 31 * less = the comparitor function to use 32 * supportGC = true if the container should support holding references to 33 * GC-allocated memory. 34 * cacheLineSize = Nodes will be sized to fit within this number of bytes. 35 * See_also: $(LINK http://en.wikipedia.org/wiki/T-tree) 36 */ 37 struct TTree(T, Allocator = Mallocator, bool allowDuplicates = false, 38 alias less = "a < b", bool supportGC = shouldAddGCRange!T, size_t cacheLineSize = 64) 39 { 40 /** 41 * T-Trees are not copyable due to the way they manage memory and interact 42 * with allocators. 43 */ 44 this(this) @disable; 45 46 static if (stateSize!Allocator != 0) 47 { 48 /// No default construction if an allocator must be provided. 49 this() @disable; 50 51 /** 52 * Use `allocator` to allocate and free nodes in the tree. 53 */ 54 this(Allocator allocator) 55 in 56 { 57 assert(allocator !is null, "Allocator must not be null"); 58 } 59 do 60 { 61 this.allocator = allocator; 62 } 63 64 private alias AllocatorType = Allocator; 65 } 66 else 67 private alias AllocatorType = void*; 68 69 ~this() @trusted 70 { 71 scope(failure) assert(false, "TTree destructor threw an exception"); 72 clear(); 73 } 74 75 /** 76 * Removes all elements from the tree. 77 */ 78 void clear() 79 { 80 _length = 0; 81 if (root is null) 82 return; 83 static if (stateSize!Allocator > 0) 84 deallocateNode(root, allocator); 85 else 86 deallocateNode(root, null); 87 } 88 89 debug(EMSI_CONTAINERS) invariant() 90 { 91 assert (root is null || _length != 0, "Empty tree with non-null root"); 92 } 93 94 /** 95 * $(B tree ~= item) operator overload. 96 */ 97 void opOpAssign(string op)(T value) if (op == "~") 98 { 99 insert(value); 100 } 101 102 /** 103 * Inserts the given value(s) into the tree. 104 * 105 * This is not a stable insert. You will get strange results if you insert 106 * into a tree while iterating over it. 107 * 108 * Params: 109 * value = the value to insert 110 * overwrite = if `true` the given `value` will replace the first item 111 * in the tree that is equivalent (That is greater-than and less-than 112 * are both false) to `value`. This is useful in cases where opCmp 113 * and opEquals for `T` type have different meanings. For example, 114 * if the element type is a circle that has a position and a color, 115 * the circle could implement `opCmp` to sort by color, and calling 116 * `insert` with `overwrite` set to `true` would allow you to update 117 * the position of the circle with a certain color in the tree. 118 * Returns: the number of values added. 119 */ 120 size_t insert(T value, bool overwrite = false) @safe 121 { 122 if (root is null) 123 { 124 static if (stateSize!Allocator > 0) 125 root = allocateNode(cast(Value) value, null, allocator); 126 else 127 root = allocateNode(cast(Value) value, null, null); 128 ++_length; 129 return true; 130 } 131 static if (stateSize!Allocator > 0) 132 immutable bool r = root.insert(cast(Value) value, root, allocator, overwrite); 133 else 134 immutable bool r = root.insert(cast(Value) value, root, null, overwrite); 135 if (r) 136 ++_length; 137 return r ? 1 : 0; 138 } 139 140 /// ditto 141 size_t insert(R)(R r, bool overwrite = false) if (isInputRange!R && is(ElementType!R == T)) 142 { 143 size_t retVal; 144 while (!r.empty) 145 { 146 retVal += insert(r.front(), overwrite); 147 r.popFront(); 148 } 149 return retVal; 150 } 151 152 /// ditto 153 size_t insert(T[] values, bool overwrite = false) 154 { 155 size_t retVal; 156 foreach (ref v; values) 157 retVal += insert(v, overwrite); 158 return retVal; 159 } 160 161 /// ditto 162 alias insertAnywhere = insert; 163 164 /// ditto 165 alias put = insert; 166 167 /** 168 * Removes a single value from the tree, or does nothing. 169 * 170 * If `allowDuplicates` is true only a single element that is equivalent to 171 * the given `value` will be removed. Which of these elements is removed is 172 * not defined. 173 * 174 * Params: 175 * value = a value equal to the one to be removed 176 * cleanup = a function that should be run on the removed item 177 * Retuns: true if any value was removed 178 */ 179 bool remove(T value, void delegate(T) cleanup = null) 180 { 181 static if (stateSize!Allocator > 0) 182 immutable bool removed = root !is null && root.remove(cast(Value) value, root, allocator, cleanup); 183 else 184 immutable bool removed = root !is null && root.remove(cast(Value) value, root, null, cleanup); 185 if (removed) 186 { 187 --_length; 188 if (_length == 0) 189 { 190 static if (stateSize!Allocator > 0) 191 deallocateNode(root, allocator); 192 else 193 deallocateNode(root, null); 194 } 195 } 196 return removed; 197 } 198 199 /** 200 * Returns: true if the tree _conains the given value 201 */ 202 bool contains(T value) const @nogc @safe 203 { 204 return root !is null && root.contains(value); 205 } 206 207 /** 208 * Returns: the number of elements in the tree. 209 */ 210 size_t length() const pure nothrow @nogc @safe @property 211 { 212 return _length; 213 } 214 215 /** 216 * Returns: true if the tree is empty. 217 */ 218 bool empty() const pure nothrow @nogc @safe @property 219 { 220 return _length == 0; 221 } 222 223 /** 224 * Returns: a range over the tree. Do not insert into the tree while 225 * iterating because you may iterate over the same value multiple times 226 * or skip some values entirely. 227 */ 228 auto opSlice(this This)() inout @trusted @nogc 229 { 230 return Range!(This)(cast(const(Node)*) root, RangeType.all, T.init); 231 } 232 233 /** 234 * Returns: a range of elements which are less than value. 235 */ 236 auto lowerBound(this This)(inout T value) inout @trusted 237 { 238 return Range!(This)(cast(const(Node)*) root, RangeType.lower, value); 239 } 240 241 /** 242 * Returns: a range of elements which are equivalent (though not necessarily 243 * equal) to value. 244 */ 245 auto equalRange(this This)(inout T value) inout @trusted 246 { 247 return Range!(This)(cast(const(Node)*) root, RangeType.equal, value); 248 } 249 250 /** 251 * Returns: a range of elements which are greater than value. 252 */ 253 auto upperBound(this This)(inout T value) inout @trusted 254 { 255 return Range!(This)(cast(const(Node)*) root, RangeType.upper, value); 256 } 257 258 /** 259 * Returns: the first element in the tree. 260 */ 261 auto front(this This)() inout pure @trusted @property 262 { 263 import std.exception : enforce; 264 265 alias CET = ContainerElementType!(This, T); 266 enforce(!empty(), "Attepted to get the front of an empty tree."); 267 Node* current = cast(Node*) root; 268 while (current.left !is null) 269 current = current.left; 270 return cast(CET) current.values[0]; 271 } 272 273 /** 274 * Returns: the last element in the tree. 275 */ 276 auto back(this This)() inout pure @trusted @property 277 { 278 import std.exception : enforce; 279 280 alias CET = ContainerElementType!(This, T); 281 enforce(!empty(), "Attepted to get the back of an empty tree."); 282 Node* current = cast(Node*) root; 283 while (current.right !is null) 284 current = current.right; 285 return cast(CET) current.values[current.nextAvailableIndex - 1]; 286 } 287 288 /** 289 * Tree range 290 */ 291 static struct Range(ThisT) 292 { 293 @disable this(); 294 295 /** 296 * Standard range operations 297 */ 298 ET front() const @property @nogc 299 { 300 return cast(typeof(return)) current.values[index]; 301 } 302 303 /// ditto 304 bool empty() const pure nothrow @nogc @safe @property 305 { 306 return current is null; 307 } 308 309 /// ditto 310 void popFront() 311 { 312 _popFront(); 313 if (current is null) 314 return; 315 with (RangeType) final switch (type) 316 { 317 case upper: 318 case all: break; 319 case equal: 320 if (_less(val, front())) 321 current = null; 322 break; 323 case lower: 324 if (!_less(front(), val)) 325 current = null; 326 break; 327 } 328 } 329 330 package(containers): 331 332 // The TreeMap container needs to be able to modify part of the tree 333 // in-place. The reason that this works is that the value part of the 334 // key-value struct contained in a TTree used by a TreeMap is not used 335 // when comparing nodes. Normal users of the containers library cannot 336 // get a reference to the elements because modifying them will violate 337 // the ordering invariant of the tree. 338 T* _containersFront() const @property @nogc @trusted 339 { 340 return cast(T*) ¤t.values[index]; 341 } 342 343 private: 344 345 alias ET = ContainerElementType!(ThisT, T); 346 347 void currentToLeftmost() @nogc 348 { 349 if (current is null) 350 return; 351 while (current.left !is null) 352 current = current.left; 353 } 354 355 void currentToLeastContaining(inout T val) 356 { 357 if (current is null) 358 return; 359 while (current !is null) 360 { 361 assert(current.registry != 0, "Empty node"); 362 auto first = current.values[0]; 363 auto last = current.values[current.nextAvailableIndex - 1]; 364 immutable bool valLessFirst = _less(val, first); 365 immutable bool valLessLast = _less(val, last); 366 immutable bool firstLessVal = _less(first, val); 367 immutable bool lastLessVal = _less(last, val); 368 if (firstLessVal && valLessLast) 369 return; 370 else if (valLessFirst) 371 current = current.left; 372 else if (lastLessVal) 373 current = current.right; 374 else 375 { 376 static if (allowDuplicates) 377 { 378 if (!valLessFirst && !firstLessVal) 379 { 380 auto c = current; 381 current = current.left; 382 currentToLeastContaining(val); 383 if (current is null) 384 current = c; 385 return; 386 } 387 else 388 return; 389 } 390 else 391 return; 392 } 393 394 } 395 } 396 397 this(inout(Node)* n, RangeType type, inout T val) @nogc 398 { 399 current = n; 400 this.type = type; 401 this.val = val; 402 with (RangeType) final switch(type) 403 { 404 case all: 405 currentToLeftmost(); 406 break; 407 case lower: 408 currentToLeftmost(); 409 if (_less(val, front())) 410 current = null; 411 break; 412 case equal: 413 currentToLeastContaining(val); 414 while (current !is null && _less(front(), val)) 415 _popFront(); 416 if (current is null || _less(front(), val) || _less(val, front())) 417 current = null; 418 break; 419 case upper: 420 currentToLeastContaining(val); 421 while (current !is null && !_less(val, front())) 422 _popFront(); 423 break; 424 } 425 } 426 427 void _popFront() @nogc 428 in 429 { 430 assert (!empty, "Calling .popFront with empty TTree"); 431 } 432 do 433 { 434 index++; 435 if (index >= nodeCapacity || current.isFree(index)) 436 { 437 index = 0; 438 if (current.right !is null) 439 { 440 current = current.right; 441 while (current.left !is null) 442 current = current.left; 443 } 444 else if (current.parent is null) 445 current = null; 446 else if (current.parent.left is current) 447 current = current.parent; 448 else 449 { 450 while (current.parent.right is current) 451 { 452 current = current.parent; 453 if (current.parent is null) 454 { 455 current = null; 456 return; 457 } 458 } 459 current = current.parent; 460 } 461 } 462 } 463 464 size_t index; 465 const(Node)* current; 466 const RangeType type; 467 const T val; 468 } 469 470 mixin AllocatorState!Allocator; 471 472 /// The number of values that can be stored in a single T-Tree node. 473 enum size_t nodeCapacity = N[0]; 474 475 private: 476 477 import containers.internal.element_type : ContainerElementType; 478 import containers.internal.node : FatNodeInfo, fullBits, shouldAddGCRange, shouldNullSlot; 479 import containers.internal.storage_type : ContainerStorageType; 480 import std.algorithm : sort; 481 import std.functional: binaryFun; 482 import std.range : ElementType, isInputRange; 483 import std.traits: isPointer, PointerTarget; 484 import std.experimental.allocator.common : stateSize; 485 486 alias N = FatNodeInfo!(T.sizeof, 3, cacheLineSize, ulong.sizeof); 487 alias Value = ContainerStorageType!T; 488 alias BookkeepingType = N[1]; 489 enum HEIGHT_BIT_OFFSET = 48UL; 490 enum fullBitPattern = fullBits!(ulong, nodeCapacity); 491 enum RangeType : ubyte { all, lower, equal, upper } 492 enum bool useGC = supportGC && shouldAddGCRange!T; 493 494 static assert (nodeCapacity <= HEIGHT_BIT_OFFSET, "cannot fit height info and registry in ulong"); 495 static assert (nodeCapacity <= (typeof(Node.registry).sizeof * 8)); 496 static assert (Node.sizeof <= cacheLineSize); 497 498 // If we're storing a struct that defines opCmp, don't compare pointers as 499 // that is almost certainly not what the user intended. 500 static if (is(typeof(less) == string )) 501 { 502 // Everything inside of this `static if` is dumb. `binaryFun` does not 503 // correctly infer nothrow and @nogc attributes, among other things, so 504 // we need to declare a function here that has its attributes properly 505 // inferred. It's not currently possible, however, to use this function 506 // with std.algorithm.sort because of symbol visibility issues. Because 507 // of this problem, keep a duplicate of the sorting predicate in string 508 // form in the `_lessStr` alias. 509 static if (less == "a < b" && isPointer!T 510 && __traits(hasMember, PointerTarget!T, "opCmp")) 511 { 512 enum _lessStr = "a.opCmp(*b) < 0"; 513 static bool _less(TT)(const TT a, const TT b) 514 { 515 return a.opCmp(*b) < 0; 516 } 517 } 518 else 519 { 520 enum _lessStr = less; 521 alias _less = binaryFun!less; 522 } 523 } 524 else 525 alias _less = binaryFun!less; 526 527 static Node* allocateNode(Value value, Node* parent, AllocatorType allocator) @trusted 528 out (result) 529 { 530 assert (result.left is null); 531 assert (result.right is null); 532 } 533 do 534 { 535 import core.memory : GC; 536 import std.experimental.allocator : make; 537 538 static if (stateSize!Allocator == 0) 539 Node* n = make!Node(Allocator.instance); 540 else 541 Node* n = make!Node(allocator); 542 n.parent = parent; 543 n.markUsed(0); 544 n.values[0] = cast(Value) value; 545 static if (useGC) 546 GC.addRange(n, Node.sizeof); 547 return n; 548 } 549 550 static void deallocateNode(ref Node* n, AllocatorType allocator) 551 in 552 { 553 assert (n !is null); 554 } 555 do 556 { 557 import std.experimental.allocator : dispose; 558 import core.memory : GC; 559 560 if (n.left !is null) 561 deallocateNode(n.left, allocator); 562 if (n.right !is null) 563 deallocateNode(n.right, allocator); 564 565 static if (useGC) 566 GC.removeRange(n); 567 static if (stateSize!Allocator == 0) 568 dispose(Allocator.instance, n); 569 else 570 dispose(allocator, n); 571 n = null; 572 } 573 574 static struct Node 575 { 576 private size_t nextAvailableIndex() const pure nothrow @nogc @safe 577 { 578 import containers.internal.backwards : bsf; 579 580 return bsf(~(registry & fullBitPattern)); 581 } 582 583 private void markUsed(size_t index) pure nothrow @nogc @safe 584 { 585 registry |= (1UL << index); 586 } 587 588 private void markUnused(size_t index) pure nothrow @nogc @safe 589 { 590 registry &= ~(1UL << index); 591 static if (shouldNullSlot!T) 592 values[index] = null; 593 } 594 595 private bool isFree(size_t index) const pure nothrow @nogc @safe 596 { 597 return (registry & (1UL << index)) == 0; 598 } 599 600 private bool isFull() const pure nothrow @nogc @safe 601 { 602 return (registry & fullBitPattern) == fullBitPattern; 603 } 604 605 private bool isEmpty() const pure nothrow @nogc @safe 606 { 607 return (registry & fullBitPattern) == 0; 608 } 609 610 bool contains(Value value) const @trusted 611 { 612 import std.range : assumeSorted; 613 size_t i = nextAvailableIndex(); 614 if (_less(value, cast(Value) values[0])) 615 return left !is null && left.contains(value); 616 if (_less(values[i - 1], value)) 617 return right !is null && right.contains(value); 618 static if (is(typeof(_lessStr))) 619 return !assumeSorted!_lessStr(values[0 .. i]).equalRange(value).empty; 620 else 621 return !assumeSorted!_less(values[0 .. i]).equalRange(value).empty; 622 } 623 624 ulong calcHeight() pure nothrow @nogc @safe 625 { 626 immutable ulong l = left !is null ? left.height() : 0; 627 immutable ulong r = right !is null ? right.height() : 0; 628 immutable ulong h = 1 + (l > r ? l : r); 629 assert (h < ushort.max, "Height overflow"); 630 registry &= fullBitPattern; 631 registry |= (h << HEIGHT_BIT_OFFSET); 632 return h; 633 } 634 635 ulong height() const pure nothrow @nogc @safe 636 { 637 return registry >>> HEIGHT_BIT_OFFSET; 638 } 639 640 int imbalanced() const pure nothrow @nogc @safe 641 { 642 if (right !is null 643 && ((left is null && right.height() > 1) 644 || (left !is null && right.height() > left.height() + 1))) 645 return 1; 646 if (left !is null 647 && ((right is null && left.height() > 1) 648 || (right !is null && left.height() > right.height() + 1))) 649 return -1; 650 return 0; 651 } 652 653 bool insert(T value, ref Node* root, AllocatorType allocator, bool overwrite) @trusted 654 in 655 { 656 static if (isPointer!T || is (T == class) || is (T == interface)) 657 assert (value !is null, "Inserting null values is not allowed"); 658 } 659 do 660 { 661 import std.algorithm : sort; 662 import std.range : assumeSorted; 663 if (!isFull()) 664 { 665 immutable size_t index = nextAvailableIndex(); 666 static if (!allowDuplicates) 667 { 668 static if (is(typeof(_lessStr))) 669 auto r = assumeSorted!_lessStr(values[0 .. index]).trisect( 670 cast(Value) value); 671 else 672 auto r = assumeSorted!_less(values[0 .. index]).trisect( 673 cast(Value) value); 674 if (!r[1].empty) 675 { 676 if (overwrite) 677 { 678 values[r[0].length] = cast(Value) value; 679 return true; 680 } 681 return false; 682 } 683 } 684 values[index] = cast(Value) value; 685 markUsed(index); 686 static if (is(typeof(_lessStr))) 687 sort!_lessStr(values[0 .. index + 1]); 688 else 689 sort!_less(values[0 .. index + 1]); 690 return true; 691 } 692 if (_less(value, values[0])) 693 { 694 if (left is null) 695 { 696 left = allocateNode(cast(Value) value, &this, allocator); 697 calcHeight(); 698 return true; 699 } 700 immutable bool b = left.insert(value, root, allocator, overwrite); 701 if (imbalanced() == -1) 702 rotateRight(root, allocator); 703 calcHeight(); 704 return b; 705 } 706 if (_less(values[$ - 1], cast(Value) value)) 707 { 708 if (right is null) 709 { 710 right = allocateNode(value, &this, allocator); 711 calcHeight(); 712 return true; 713 } 714 immutable bool b = right.insert(value, root, allocator, overwrite); 715 if (imbalanced() == 1) 716 rotateLeft(root, allocator); 717 calcHeight(); 718 return b; 719 } 720 static if (!allowDuplicates) 721 { 722 static if (is(typeof(_lessStr))) 723 { 724 if (!assumeSorted!_lessStr(values[]).equalRange(cast(Value) value).empty) 725 return false; 726 } 727 else 728 { 729 if (!assumeSorted!_less(values[]).equalRange(cast(Value) value).empty) 730 return false; 731 } 732 } 733 734 Value[nodeCapacity + 1] temp = void; 735 temp[0 .. $ - 1] = values[]; 736 temp[$ - 1] = cast(Value) value; 737 static if (is(typeof(_lessStr))) 738 sort!_lessStr(temp[]); 739 else 740 sort!_less(temp[]); 741 if (right is null) 742 { 743 values[] = temp[0 .. $ - 1]; 744 right = allocateNode(temp[$ - 1], &this, allocator); 745 return true; 746 } 747 if (left is null) 748 { 749 values[] = temp[1 .. $]; 750 left = allocateNode(temp[0], &this, allocator); 751 return true; 752 } 753 if (right.height < left.height) 754 { 755 values[] = temp[0 .. $ - 1]; 756 immutable bool b = right.insert(temp[$ - 1], root, allocator, overwrite); 757 if (imbalanced() == 1) 758 rotateLeft(root, allocator); 759 calcHeight(); 760 return b; 761 } 762 values[] = temp[1 .. $]; 763 immutable bool b = left.insert(temp[0], root, allocator, overwrite); 764 if (imbalanced() == -1) 765 rotateRight(root, allocator); 766 calcHeight(); 767 return b; 768 } 769 770 bool remove(Value value, ref Node* n, AllocatorType allocator, 771 void delegate(T) cleanup = null) 772 { 773 import std.range : assumeSorted; 774 assert (!isEmpty(), "Calling .remove on an empty TTree.Node"); 775 if (isFull() && _less(value, values[0])) 776 { 777 immutable bool r = left !is null && left.remove(value, left, allocator, cleanup); 778 if (left.isEmpty()) 779 deallocateNode(left, allocator); 780 return r; 781 } 782 if (isFull() && _less(values[$ - 1], value)) 783 { 784 immutable bool r = right !is null && right.remove(value, right, allocator, cleanup); 785 if (right.isEmpty()) 786 deallocateNode(right, allocator); 787 return r; 788 } 789 size_t i = nextAvailableIndex(); 790 static if (is(typeof(_lessStr))) 791 auto sv = assumeSorted!_lessStr(values[0 .. i]); 792 else 793 auto sv = assumeSorted!_less(values[0 .. i]); 794 auto tri = sv.trisect(value); 795 if (tri[1].length == 0) 796 return false; 797 // Clean up removed item 798 if (cleanup !is null) 799 cleanup(tri[1][0]); 800 immutable size_t l = tri[0].length; 801 if (right is null && left is null) 802 { 803 Value[nodeCapacity - 1] temp; 804 temp[0 .. l] = values[0 .. l]; 805 temp[l .. $] = values[l + 1 .. $]; 806 values[0 .. $ - 1] = temp[]; 807 markUnused(nextAvailableIndex() - 1); 808 } 809 else if (right !is null) 810 { 811 Value[nodeCapacity - 1] temp; 812 temp[0 .. l] = values[0 .. l]; 813 temp[l .. $] = values[l + 1 .. $]; 814 values[0 .. $ - 1] = temp[]; 815 values[$ - 1] = right.removeSmallest(allocator); 816 if (right.isEmpty()) 817 deallocateNode(right, allocator); 818 } 819 else if (left !is null) 820 { 821 Value[nodeCapacity - 1] temp; 822 temp[0 .. l] = values[0 .. l]; 823 temp[l .. $] = values[l + 1 .. $]; 824 values[1 .. $] = temp[]; 825 values[0] = left.removeLargest(allocator); 826 if (left.isEmpty()) 827 deallocateNode(left, allocator); 828 } 829 return true; 830 } 831 832 Value removeSmallest(AllocatorType allocator) 833 in 834 { 835 assert (!isEmpty(), "Calling .removeSmallest on an empty TTree.Node"); 836 } 837 do 838 { 839 if (left is null && right is null) 840 { 841 Value r = values[0]; 842 Value[nodeCapacity - 1] temp = void; 843 temp[] = values[1 .. $]; 844 values[0 .. $ - 1] = temp[]; 845 markUnused(nextAvailableIndex() - 1); 846 return r; 847 } 848 if (left !is null) 849 { 850 auto r = left.removeSmallest(allocator); 851 if (left.isEmpty()) 852 deallocateNode(left, allocator); 853 return r; 854 } 855 Value r = values[0]; 856 Value[nodeCapacity - 1] temp = void; 857 temp[] = values[1 .. $]; 858 values[0 .. $ - 1] = temp[]; 859 values[$ - 1] = right.removeSmallest(allocator); 860 if (right.isEmpty()) 861 deallocateNode(right, allocator); 862 return r; 863 } 864 865 Value removeLargest(AllocatorType allocator) 866 in 867 { 868 assert (!isEmpty(), "Calling .removeLargest on an empty TTree.Node"); 869 } 870 out (result) 871 { 872 static if (isPointer!T || is (T == class) || is(T == interface)) 873 assert (result !is null, "Removed a null value"); 874 } 875 do 876 { 877 if (left is null && right is null) 878 { 879 immutable size_t i = nextAvailableIndex() - 1; 880 Value r = values[i]; 881 markUnused(i); 882 return r; 883 } 884 if (right !is null) 885 { 886 auto r = right.removeLargest(allocator); 887 if (right.isEmpty()) 888 deallocateNode(right, allocator); 889 return r; 890 } 891 Value r = values[$ - 1]; 892 Value[nodeCapacity - 1] temp = void; 893 temp[] = values[0 .. $ - 1]; 894 values[1 .. $] = temp[]; 895 values[0] = left.removeLargest(allocator); 896 if (left.isEmpty()) 897 deallocateNode(left, allocator); 898 return r; 899 } 900 901 void rotateLeft(ref Node* root, AllocatorType allocator) @safe 902 { 903 Node* newRoot; 904 if (right.left !is null && right.right is null) 905 { 906 newRoot = right.left; 907 newRoot.parent = this.parent; 908 newRoot.left = &this; 909 newRoot.left.parent = newRoot; 910 newRoot.right = right; 911 newRoot.right.parent = newRoot; 912 newRoot.right.left = null; 913 right = null; 914 left = null; 915 } 916 else 917 { 918 newRoot = right; 919 newRoot.parent = this.parent; 920 right = newRoot.left; 921 if (right !is null) 922 right.parent = &this; 923 newRoot.left = &this; 924 this.parent = newRoot; 925 } 926 cleanup(newRoot, root, allocator); 927 } 928 929 void rotateRight(ref Node* root, AllocatorType allocator) @safe 930 { 931 Node* newRoot; 932 if (left.right !is null && left.left is null) 933 { 934 newRoot = left.right; 935 newRoot.parent = this.parent; 936 newRoot.right = &this; 937 newRoot.right.parent = newRoot; 938 newRoot.left = left; 939 newRoot.left.parent = newRoot; 940 newRoot.left.right = null; 941 left = null; 942 right = null; 943 } 944 else 945 { 946 newRoot = left; 947 newRoot.parent = this.parent; 948 left = newRoot.right; 949 if (left !is null) 950 left.parent = &this; 951 newRoot.right = &this; 952 this.parent = newRoot; 953 } 954 cleanup(newRoot, root, allocator); 955 } 956 957 void cleanup(Node* newRoot, ref Node* root, AllocatorType allocator) @safe 958 { 959 if (newRoot.parent !is null) 960 { 961 if (newRoot.parent.right is &this) 962 newRoot.parent.right = newRoot; 963 else 964 newRoot.parent.left = newRoot; 965 } 966 else 967 root = newRoot; 968 newRoot.fillFromChildren(root, allocator); 969 if (newRoot.left !is null) 970 { 971 newRoot.left.fillFromChildren(root, allocator); 972 } 973 if (newRoot.right !is null) 974 { 975 newRoot.right.fillFromChildren(root, allocator); 976 } 977 if (newRoot.left !is null) 978 newRoot.left.calcHeight(); 979 if (newRoot.right !is null) 980 newRoot.right.calcHeight(); 981 newRoot.calcHeight(); 982 } 983 984 void fillFromChildren(ref Node* root, AllocatorType allocator) @trusted 985 { 986 while (!isFull()) 987 { 988 if (left !is null) 989 { 990 insert(left.removeLargest(allocator), root, allocator, false); 991 if (left.isEmpty()) 992 deallocateNode(left, allocator); 993 } 994 else if (right !is null) 995 { 996 insert(right.removeSmallest(allocator), root, allocator, false); 997 if (right.isEmpty()) 998 deallocateNode(right, allocator); 999 } 1000 else 1001 return; 1002 } 1003 } 1004 1005 debug(EMSI_CONTAINERS) invariant() 1006 { 1007 import std..string : format; 1008 assert (&this !is null, "Null this"); 1009 assert (left !is &this, "%x, %x".format(left, &this)); 1010 assert (right !is &this, "%x, %x".format(right, &this)); 1011 if (left !is null) 1012 { 1013 assert (left.left !is &this, "%s".format(values)); 1014 assert (left.right !is &this, "%x, %x".format(left.right, &this)); 1015 assert (left.parent is &this, "%x, %x, %x".format(left, left.parent, &this)); 1016 } 1017 if (right !is null) 1018 { 1019 assert (right.left !is &this, "%s".format(values)); 1020 assert (right.right !is &this, "%s".format(values)); 1021 assert (right.parent is &this, "%x, %x, %x".format(right, right.parent, &this)); 1022 } 1023 } 1024 1025 Value[nodeCapacity] values; 1026 Node* left; 1027 Node* right; 1028 Node* parent; 1029 ulong registry = 1UL << HEIGHT_BIT_OFFSET; 1030 } 1031 1032 size_t _length; 1033 Node* root; 1034 } 1035 1036 version(emsi_containers_unittest) unittest 1037 { 1038 import core.memory : GC; 1039 import std.algorithm : equal, sort, map, filter, each; 1040 import std.array : array; 1041 import std.range : iota, walkLength, isInputRange; 1042 import std..string : format; 1043 import std.uuid : randomUUID; 1044 1045 { 1046 TTree!int kt; 1047 assert (kt.empty); 1048 foreach (i; 0 .. 200) 1049 assert (kt.insert(i)); 1050 assert(kt.front == 0); 1051 assert(kt.back == 199); 1052 assert(!kt.empty); 1053 assert(kt.length == 200); 1054 assert(kt.contains(30)); 1055 } 1056 1057 { 1058 TTree!int kt; 1059 assert (!kt.contains(5)); 1060 kt.insert(2_000); 1061 assert (kt.contains(2_000)); 1062 foreach_reverse (i; 0 .. 1_000) 1063 { 1064 assert (kt.insert(i)); 1065 } 1066 assert (!kt.contains(100_000)); 1067 } 1068 1069 { 1070 import std.random : uniform; 1071 TTree!int kt; 1072 foreach (i; 0 .. 1_000) 1073 kt.insert(uniform(0, 100_000)); 1074 } 1075 1076 { 1077 TTree!int kt; 1078 kt.insert(10); 1079 assert (kt.length == 1); 1080 assert (!kt.insert(10)); 1081 assert (kt.length == 1); 1082 } 1083 1084 { 1085 TTree!(int, Mallocator, true) kt; 1086 assert (kt.insert(1)); 1087 assert (kt.length == 1); 1088 assert (kt.insert(1)); 1089 assert (kt.length == 2); 1090 assert (kt.contains(1)); 1091 } 1092 1093 { 1094 TTree!(int) kt; 1095 foreach (i; 0 .. 200) 1096 assert (kt.insert(i)); 1097 assert (kt.length == 200); 1098 assert (kt.remove(79)); 1099 assert (!kt.remove(79)); 1100 assert (kt.length == 199); 1101 } 1102 1103 { 1104 string[] strs = [ 1105 "2c381d2a-bacd-40db-b6d8-055b144c5ee6", 1106 "62104b50-e235-4c95-bcb9-a545e88e2d09", 1107 "828c8fc0-a392-4738-a49c-62e991fce090", 1108 "62e30465-79eb-446e-b34f-af5d7c491486", 1109 "93ec245b-60d2-4422-91ff-66a6d7e299fc", 1110 "c1d2f3d7-82cc-4d90-a2c5-9fba335f36cd", 1111 "c9d8d980-94eb-4941-b873-00d68021522f", 1112 "82dbc4df-cb3c-447a-9d73-cd6291a0ba02", 1113 "8d259231-6ab6-49e4-9bb6-fe097c4153ed", 1114 "f9f2d719-61e1-4f62-ae2c-bf2a24a13d5b" 1115 ]; 1116 TTree!string strings; 1117 foreach (i, s; strs) 1118 assert (strings.insert(s)); 1119 sort(strs[]); 1120 assert (equal(strs, strings[])); 1121 } 1122 1123 foreach (x; 0 .. 1000) 1124 { 1125 TTree!string strings; 1126 string[] strs = iota(10).map!(a => randomUUID().toString()).array(); 1127 foreach (i, s; strs) 1128 assert (strings.insert(s)); 1129 assert (strings.length == strs.length); 1130 sort(strs); 1131 assert (equal(strs, strings[])); 1132 } 1133 1134 { 1135 TTree!string strings; 1136 strings.insert(["e", "f", "a", "b", "c", "d"]); 1137 assert (equal(strings[], ["a", "b", "c", "d", "e", "f"])); 1138 } 1139 1140 { 1141 TTree!(string, Mallocator, true) strings; 1142 assert (strings.insert("b")); 1143 assert (strings.insert("c")); 1144 assert (strings.insert("a")); 1145 assert (strings.insert("d")); 1146 assert (strings.insert("d")); 1147 assert (strings.length == 5); 1148 assert (equal(strings.equalRange("d"), ["d", "d"]), format("%s", strings.equalRange("d"))); 1149 assert (equal(strings.equalRange("a"), ["a"]), format("%s", strings.equalRange("a"))); 1150 assert (equal(strings.lowerBound("d"), ["a", "b", "c"]), format("%s", strings.lowerBound("d"))); 1151 assert (equal(strings.upperBound("c"), ["d", "d"]), format("%s", strings.upperBound("c"))); 1152 } 1153 1154 { 1155 static struct S 1156 { 1157 string x; 1158 int opCmp (ref const S other) const @nogc 1159 { 1160 if (x < other.x) 1161 return -1; 1162 if (x > other.x) 1163 return 1; 1164 return 0; 1165 } 1166 } 1167 1168 TTree!(S*, Mallocator, true) stringTree; 1169 auto one = S("offset"); 1170 stringTree.insert(&one); 1171 auto two = S("object"); 1172 assert (stringTree.equalRange(&two).empty); 1173 assert (!stringTree.equalRange(&one).empty); 1174 assert (stringTree[].front.x == "offset"); 1175 } 1176 1177 { 1178 static struct TestStruct 1179 { 1180 int opCmp(ref const TestStruct other) const @nogc 1181 { 1182 return x < other.x ? -1 : (x > other.x ? 1 : 0); 1183 } 1184 int x; 1185 int y; 1186 } 1187 TTree!(TestStruct*) tsTree; 1188 static assert (isInputRange!(typeof(tsTree[]))); 1189 foreach (i; 0 .. 100) 1190 assert(tsTree.insert(new TestStruct(i, i * 2))); 1191 assert (tsTree.length == 100); 1192 auto r = tsTree[]; 1193 TestStruct* prev = r.front(); 1194 r.popFront(); 1195 while (!r.empty) 1196 { 1197 assert (r.front.x > prev.x, format("%s %s", prev.x, r.front.x)); 1198 prev = r.front; 1199 r.popFront(); 1200 } 1201 TestStruct a = TestStruct(30, 100); 1202 auto eqArray = array(tsTree.equalRange(&a)); 1203 assert (eqArray.length == 1, format("%d", eqArray.length)); 1204 } 1205 1206 { 1207 import std.algorithm : canFind; 1208 TTree!int ints; 1209 foreach (i; 0 .. 50) 1210 ints ~= i; 1211 assert (canFind(ints[], 20)); 1212 assert (walkLength(ints[]) == 50); 1213 assert (walkLength(filter!(a => (a & 1) == 0)(ints[])) == 25); 1214 } 1215 1216 { 1217 TTree!int ints; 1218 foreach (i; 0 .. 50) 1219 ints ~= i; 1220 ints.remove(0); 1221 assert (ints.length == 49); 1222 foreach (i; 1 .. 12) 1223 ints.remove(i); 1224 assert (ints.length == 49 - 11); 1225 } 1226 1227 { 1228 const(TTree!(const(int))) getInts() 1229 { 1230 TTree!(const(int)) t; 1231 t.insert(1); 1232 t.insert(2); 1233 t.insert(3); 1234 return t; 1235 } 1236 auto t = getInts(); 1237 static assert (is (typeof(t[].front) == const(int))); 1238 assert (equal(t[].filter!(a => a & 1), [1, 3])); 1239 } 1240 1241 1242 { 1243 static struct ABC 1244 { 1245 ulong a; 1246 ulong b; 1247 1248 int opCmp(ref const ABC other) const @nogc 1249 { 1250 if (this.a < other.a) 1251 return -1; 1252 if (this.a > other.a) 1253 return 1; 1254 return 0; 1255 } 1256 } 1257 1258 TTree!(ABC, Mallocator, true) tree; 1259 foreach (i; 0 .. 10) 1260 tree.insert(ABC(i)); 1261 tree.insert(ABC(15)); 1262 tree.insert(ABC(15)); 1263 tree.insert(ABC(15)); 1264 tree.insert(ABC(15)); 1265 foreach (i; 20 .. 30) 1266 tree.insert(ABC(i)); 1267 assert(tree.equalRange(ABC(15)).walkLength() == 4, 1268 format("Actual length = %d", tree.equalRange(ABC(15)).walkLength())); 1269 } 1270 1271 { 1272 TTree!int ints2; 1273 iota(0, 1_000_000).each!(a => ints2.insert(a)); 1274 assert(equal(iota(0, 1_000_000), ints2[])); 1275 assert(ints2.length == 1_000_000); 1276 foreach (i; 0 .. 1_000_000) 1277 assert(!ints2.equalRange(i).empty, format("Could not find %d", i)); 1278 } 1279 1280 { 1281 TTree!int ints3; 1282 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 0)) 1283 ints3.insert(i); 1284 assert(ints3.length == 500_000); 1285 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 0)) 1286 assert(!ints3.equalRange(i).empty); 1287 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 1)) 1288 assert(ints3.equalRange(i).empty); 1289 } 1290 1291 { 1292 TTree!(ubyte, Mallocator, true) ubytes; 1293 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 0).map!(a => cast(ubyte)(a % ubyte.max))) 1294 ubytes.insert(i); 1295 assert(ubytes[].walkLength == 500_000, "%d".format(ubytes[].walkLength)); 1296 assert(ubytes.length == 500_000, "%d".format(ubytes.length)); 1297 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 0).map!(a => cast(ubyte)(a % ubyte.max))) 1298 assert(!ubytes.equalRange(i).empty); 1299 } 1300 1301 { 1302 import std.experimental.allocator.building_blocks.free_list : FreeList; 1303 import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; 1304 import std.experimental.allocator.building_blocks.region : Region; 1305 import std.experimental.allocator.building_blocks.stats_collector : StatsCollector; 1306 import std.stdio : stdout; 1307 1308 StatsCollector!(FreeList!(AllocatorList!(a => Region!(Mallocator)(1024 * 1024)), 1309 64)) allocator; 1310 { 1311 auto ints4 = TTree!(int, typeof(&allocator))(&allocator); 1312 foreach (i; 0 .. 10_000) 1313 ints4.insert(i); 1314 assert(walkLength(ints4[]) == 10_000); 1315 } 1316 assert(allocator.numAllocate == allocator.numDeallocate); 1317 assert(allocator.bytesUsed == 0); 1318 } 1319 } 1320 1321 version(emsi_containers_unittest) unittest 1322 { 1323 static class Foo 1324 { 1325 string name; 1326 1327 this(string s) 1328 { 1329 this.name = s; 1330 } 1331 } 1332 1333 TTree!(Foo, Mallocator, false, "a.name < b.name") tt; 1334 auto f = new Foo("foo"); 1335 tt.insert(f); 1336 f = new Foo("bar"); 1337 tt.insert(f); 1338 auto r = tt[]; 1339 } 1340 1341 version(emsi_containers_unittest) unittest 1342 { 1343 import std.range : walkLength; 1344 import std.stdio; 1345 1346 TTree!(int, Mallocator, true) tt; 1347 tt.insert(10); 1348 tt.insert(11); 1349 tt.insert(12); 1350 assert(tt.length == 3); 1351 tt.insert(11); 1352 assert(tt.length == 4); 1353 tt.remove(11); 1354 assert(tt.length == 3); 1355 assert(tt[].walkLength == tt.length); 1356 }